//package cn.zyq.charging.cost.service.impl;
//
//import cn.zyq.charging.common.pojo.param.ChargingProcessParam;
//import cn.zyq.charging.common.pojo.vo.ChargingProcessVO;
//import cn.zyq.charging.cost.dao.repository.CostRuleRepository;
//import cn.zyq.charging.cost.pojo.ChargingData;
//import cn.zyq.charging.cost.pojo.po.CostRulePO;
//import cn.zyq.charging.cost.service.CostService;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.stereotype.Service;
//import org.springframework.util.CollectionUtils;
//
//import java.math.BigDecimal;
//import java.time.Duration;
//import java.time.LocalDateTime;
//import java.util.Collections;
//import java.util.HashMap;
//import java.util.List;
//import java.util.Map;
//
////计价流程：
////1.用户手机扫码调用订单服务生成订单
////2.订单服务调用设备服务更新充电枪状态。
////3.订单服务然后给设备发送充电指令。
////4.设备执行充电指令，并给订单服务返回充电成功或失败的结果，
////5.订单服务给mysql插入一条订单数据。
////6.设备间隔给订单服务同步充电数据。
////7.订单服务调用计价服务计算充电费用。
////8.计价服务返回充电费用给订单服务，订单服务再返回充电费用给用户。
//@Slf4j
//@Service
//public class CostServiceImpl2_bak implements CostService {
//
//    /**
//     * Map集合中存储了每个订单的充电信息，每个订单对应一个ChargingData对象(key是订单号 value是充电数据)，
//     * 每次电量更新都会修改Map集合中key对应的value(ChargingData对象)信息，
//     * (下一次充电的总度数是上一次充电的电量加上本次的充电量)
//     */
//    private Map<String, ChargingData> chargingProcessData = new HashMap<>();
//    //后续会将这个Map数据存入到ES中(删除这个Map集合)(避免计价服务宕机或多个计价服务数据一致性的问题)。
//
//    /**
//     * 价格计算
//     * 1.什么样的因素会影响价格
//     *   1.1. 时间段不同价格不同：  晚上便宜 低峰 白天贵 高峰
//     *   1.2. 地理位置：  充电站站不同有可能价格不同(有些地区或运营商会偏贵)
//     *   1.3. 枪的类型：  快充就贵，  慢充就便宜
//     *   1.4. 充电量
//     * 2. 设备多次同步充电进度,多次怎么算?
//     *   2.1 取最后一次 怎么确定是最后一次 ?
//     *     2.1.1 判断是否是第一次或者是第N次
//     *     次数            充电度数 价格
//     *     第1次同步充电数据  10     20
//     *     第2次同步充电数据  20     40 (如果第二次的20度电包含了第一次的10度，第2次同步充电数据时，充电的总度数就是20)
//     *     第3次同步充电数据  29     58 (如果第三次的29度电包含了第二次的20度，第3次同步充电数据时，充电的总度数就是29)
//     *     第4次同步充电数据  40     80 (如果第四次的40度电包含了第三次的29度，第4次同步充电数据时，充电的总度数就是40)
//     *     则总共充了40度电(最后一次同步的充电度数就是充电的总度数， 因为最后一次同步的度数包含了前三次所有的充电度数<不同和前边三次累加>)。
//     *
//     *     注意： 反过来，如果每次只是同步上一次到本次同步数据之间的时间段内的充电度数，则第一次同步时冲了10度电，第二次同步时冲了20度电，
//     *           第三次同步时冲了29度电，第四次同步时冲了40度电， 这四次总共充了99度电。
//     *   思考 如果你是设备的设计者,  第2次同步的充电度数  是否应该包含 第1次的充电度数 ?
//     *    答案： 包含或者不包含都可以，但是每次同步充电数据都不包含上一次的充电度数就太复杂了 代码实现困难太大(必须保证Mqtt消息不丢失)，
//     *          假如丢失一次就损失运营成本了<会造成经济损失>。    所以每次同步充电数据都包含上一次充电的总度数，更容易实现一些。
//     * @param chargingProcessParam 封装充电进度的对象。
//     * @return 返回封装充电进度的vo对象。
//     */
//    public ChargingProcessVO calculateCost(ChargingProcessParam chargingProcessParam) {
//        //1.从Map集合中获取chargingData
//        //ChargingData chargingData = chargingProcessData.get(orderNo);//根据订单号从Map集合中获取chargingData
//        //2.如果获取不到就创建一个chargingData放入到Map集合。
//        //这个if通过从map中获取ChargingData，如果获取不到就是第一次同步充电数据，就创建chargingData(并设置订单id和开始充电时间)存入到Map集合中。
//        /*if(chargingData==null){//用于判断是否同步充电进度数据了
//            chargingData = getChargingData(orderNo);
//        }*/
//        //上边可以将chargingData==null这个if优化到getChargingData()方法中。
//
//        //1.从Map集合中获取chargingData，如果获取不到就创建一个chargingData放入到Map集合。
//        ChargingData chargingData = buildChargingData(chargingProcessParam);
//
//        //2.根据充电站id去mysql数据库获取计价列表
//        Integer stationId = chargingProcessParam.getStationId();//获取充电站id
//        log.debug("获取计价规则:入参:{}",stationId);
//
//
//
//        /*List<CostRulePO> costRules=getCostRule(stationId);//通过充电站站id去mysql数据库获取计价列表
//        log.debug("获取计价规则:入参:{},出参:{}",stationId,costRules);
//        //当前时间
//        LocalDateTime now = LocalDateTime.now();
//        //当前时间中的小时数值
//        int hour = now.getHour();
//        log.debug("获取当前时间(主要是获取其中的小时)，根据当前时间的小时数看属于哪个计价规则(尖峰平谷):{}",hour);
//        log.debug("获取当前时间 小时 :{}  判断当前小时 属于 哪个计价的时间段 ( 尖 峰 平 谷 )计算价格",hour);
//        //3.迭代查找计价规则并计算本次充电费用，然后累加计算充电总费用。
//        //具体逻辑：循环当前计价列表，判断本次同步的充电数据属于哪个计价规则，然后用计价规则计算本次的充电费用，然后用本次的充电费用和上一次的充电总费用累加，并存入到chargingData中。
//        for(CostRulePO costRulePO:costRules){
//            //判断当前小时属于哪个时间段
//            //if(当前时间 大于等于计价规则的开始时间   &&  小于 结束时间)
//            if(costRulePO.getStartTime() <= hour  && hour< costRulePO.getEndTime()){//判断同步的充电进度数据是否是第一次的数据。
//                String ruleName= costRulePO.getName();//计价规则名称
//                BigDecimal powerFee= costRulePO.getPowerFee();
//                chargingData.setCurrentOneFee(powerFee);//给充电记录数据设置电费单价
//                log.debug("当前时间(小时):{},业务时间段名称(计价规则名称):{},电费单价:{}",hour,ruleName,powerFee);
//                if(chargingData.getLastChargingProcessParam()==null){//如果是第一次同步充电数据，则设置充电记录数据中的上一次充电信息为当前充电信息。
//                    //getFirstCost(chargingProcessParam, powerFee, hour, ruleName, chargingData);
//                    //将上边方法参数顺序调整一下(基本类型的参数往后写)
//                    appendFirstCostToChargingData(chargingProcessParam, chargingData, powerFee, hour, ruleName);
//                }else{
//                    appendLastCostToChargingData(chargingProcessParam, chargingData, powerFee, hour, ruleName);
//                }
//                break;//如果匹配到计价规则后就会结束循环(不要再执行多余的循环了)//当前同步数据的时间，只会处于一个计价规则的时间段内，匹配到一个计价规则后，无需继续循环 就用break退出循环。
//            }
//        }*/
//        //将上for循环提取到一个getMatchCostRuleByTime()方法中。上边代码修改成下边格式了
//
//
//        LocalDateTime now = LocalDateTime.now();
//        //当前时间中的小时数值
//        int hour = now.getHour();
//        CostRulePO costRulePO = getMatchCostRuleByTime(stationId, hour);
//        log.debug("通过场站信息:{},充电同步时间:{},获取计价规则{}",stationId,hour,costRulePO);
//        if(costRulePO!=null){
//            String ruleName= costRulePO.getName();//计价规则名称
//            BigDecimal powerFee= costRulePO.getPowerFee();//从计价规则中获取单位电价。
//            chargingData.setCurrentOneFee(powerFee);//给充电记录数据设置电费单价
//            log.debug("当前时间(小时):{},业务时间段名称(计价规则名称):{},电费单价:{}",hour,ruleName,powerFee);
//            if(chargingData.getLastChargingProcessParam()==null){//如果是第一次同步充电数据，则设置充电记录数据中的上一次充电信息为当前充电信息。
//                //getFirstCost(chargingProcessParam, powerFee, hour, RuleName, chargingData);
//                //将上边方法参数顺序调整一下(基本类型的参数往后写)
//                appendFirstCostToChargingData(chargingProcessParam, chargingData, powerFee, hour, ruleName);
//            }else{
//                appendLastCostToChargingData(chargingProcessParam, chargingData, powerFee, hour, ruleName);
//            }
//        }else{
//            //如果没有获取到计价规则,需要告警,接入监控系统,停止充电了.返回空对象
//            log.error("获取计价规则失败,告警告警告警");
//            return null;
//        }
//
//
//        chargingData.setTotalCapacity(chargingProcessParam.getChargingCapacity());//设置充电总度数
//        //订单编号和充电枪id在buildChargingData(chargingProcessParam)中已经有了，所以这两行不用写了
//        //chargingData.setOrderNo(chargingProcessParam.getOrderNo());
//        //chargingData.setGunId(chargingProcessParam.getGunId());
//        return buildChargingProcessVO(chargingData, chargingProcessParam);
//    }
//
//
//
////    //上边方法是优化后的只起一个框架的作用，具体的解释及详细代码实现在本单行注释中。
////    public ChargingProcessVO calculateCost(ChargingProcessParam chargingProcessParam) {
////        /*//通过chargingProcessParam入参订单号，从map集合中获取充电数据，有就返回，没有就初始化并放入到map集合中。
////        ChargingData chargingData = buildChargingData(chargingProcessParam);
////        //需要判断chargingProcessParam是第一次还是第二次充电的数据。、、如果不是第一次充电，则需要用Map集合中的ChargingData信息和
////        //   本次的充电信息ChargingProcessParam累加来实现计算充电的累加和。*/
////
////
////
////        //1.从Map集合中获取chargingData
////        String orderNo = chargingProcessParam.getOrderNo();//获取订单号
////        ChargingData chargingData = chargingProcessData.get(orderNo);//根据订单号从Map集合中获取chargingData
////        //2.如果获取不到就创建一个chargingData放入到Map集合。
////        //这个if通过从map中获取ChargingData，如果获取不到就是第一次同步充电数据，就创建chargingData(并设置订单id和开始充电时间)存入到Map集合中。
////        if(chargingData==null){
////            chargingData = getChargingData(orderNo);
////
////            //将下边代码全部提取到了getChargingData()方法中了
////            /*ChargingData chargingData= new ChargingData();
////            log.debug("通过订单号:{}，从Map集合中没有获取到充电数据，表示本次是第一次同步充电数据", orderNo);
////            //第一次：  如果是第一次同步充电数据。
////            chargingData.setOrderNo(orderNo);
////            //BigDecimal cost = getCost(chargingProcessParam);//获取本次的充电度数
////            //把当前当前的充电度数(也是充电总金额)设置到chargingData中
////            //注意： 讲师为什么这样设置价格不知道，但是如果1块钱一度电，则充电的总度数就是充电的总金额
////            //chargingData.setTotalCost(cost);
////            //设置开始充电时间。
////            chargingData.setStartTime(LocalDateTime.now());
////            //本订单的充电数据对象chargingData保存到Map集合中。
////            chargingProcessData.put(orderNo,chargingData);*/
////        }
////        //2.根据充电站id去mysql数据库获取计价列表
////        Integer stationId = chargingProcessParam.getStationId();//获取充电站id
////        log.debug("获取计价规则:入参:{}",stationId);
////        List<CostRulePO> costRules=getCostRule(stationId);//通过充电站站id去mysql数据库获取计价列表
////        log.debug("获取计价规则:入参:{},出参:{}",stationId,costRules);
////        //当前时间
////        LocalDateTime now = LocalDateTime.now();
////        //当前时间中的小时数值
////        int hour = now.getHour();
////        log.debug("获取当前时间(主要是获取其中的小时)，根据当前时间的小时数看属于哪个计价规则(尖峰平谷):{}",hour);
////        log.debug("获取当前时间 小时 :{}  判断当前小时 属于 哪个计价的时间段 ( 尖 峰 平 谷 )计算价格",hour);
////        //3.迭代查找计价规则并计算本次充电费用，然后累加计算充电总费用。
////        //具体逻辑：循环当前计价列表，判断本次同步的充电数据属于哪个计价规则，然后用计价规则计算本次的充电费用，然后用本次的充电费用和上一次的充电总费用累加，并存入到chargingData中。
////        for(CostRulePO costRulePO:costRules){
////            //判断当前小时属于哪个时间段
////            //if(当前时间 大于等于计价规则的开始时间   &&  小于 结束时间)
////            if(costRulePO.getStartTime() <= hour  && hour< costRulePO.getEndTime()){
////                String RuleName= costRulePO.getName();//计价规则名称
////                BigDecimal powerFee= costRulePO.getPowerFee();
////                chargingData.setCurrentOneFee(powerFee);//给充电记录数据设置电费单价
////                log.debug("当前时间(小时):{},业务时间段名称(计价规则名称):{},电费单价:{}",hour,RuleName,powerFee);
////                //两种方式判断是否是第一次同步充电进度数据：
////                //第一种方式用Map集合存储ChargingData对象-key用orderNo，如果集合中没有此对象就是第一次同步充电进度数据。
////                //第二种方式： 在ChargingData对象中保存上一次同步充电进度数据。 如果从chargingData中获取不到上一次同步充电进度数据，
////                // 则ChargingData对象可能就是刚被创建，还未放入到Map集合中呢，表示本次是第一次同步充电进度数据。
////                if(chargingData.getLastChargingProcessParam()==null){//如果是第一次同步充电数据，则设置充电记录数据中的上一次充电信息为当前充电信息。
////                    /*//这个if通过从map中获取ChargingData，然后从chargingData中获取上一次同步的充电数据<如果没有则表示第一次同步充电数据>。
////                    //这个if和上边的if是重复的逻辑代码，后边会简化掉。
////                    // (A.因为如果是第一次同步充电数据则Map集合中就不会有这个ChargingData对象<表示第一次同步充电数据，但还没有向Map集合中放ChargingData>,
////                    //  B.如果Map中有ChargingData对象但此对象中没有LastChargingProcessParam，则表示第一次同步充电数据后，已经给Map集合中放了ChargingData)
////                    //  这两种情况都表示第一次同步充电数据，所以是重复的。
////                    log.debug("通过订单号orderNo从充电记录对象chargingData中获取上一次同步的充电数据， 如果获取不到就表示是第一次同步充电数据11");
////                    //获取充电度数。
////
////                    //1.获取本次同步的充电度数(这个if分支表示第一次同步充电数据，所以这个充电度数就是本次实际的充电度数)
////                    float chargingCapacity = chargingProcessParam.getChargingCapacity();
////                    //把float类型的充电度数chargingCapacity转换为BigDecimal类型的chargingCapacityBigDecimal。
////
////
//////                    BigDecimal chargingCapacityBigDecimal = new BigDecimal(Float.toString(chargingCapacity));
////                    //上边类型转化的代码不需要了(使用了getCost()方法就不需要这个代码了被删除了)
////
////                    //单价powerFee  * 度数chargingCapacityBigDecimal = 本次充电费用
////                    //但是有个问题：充电时间比较长，有可能会跨越多个计价规则，每个计价规则单位电价不同，所以计算本次充电总费用计算稿方式需要调整成下边格式：
////                    //平时间段的时长  * 平时间段的电价   +     尖下午时间段的时长  * 尖下午时间段的电价 +   峰时间段的时长  * 峰时间段的电价  = 本次充电费用
////
////
//////                    BigDecimal chargingCost = powerFee.multiply(chargingCapacityBigDecimal);
////                    //优化成下边格式//使用getCost()封装乘法了
////                    //2.用getCost()计算本次的充电费用chargingCost ： 单位电价 * 本次充电度数
////                    //注意： 因为这个if表示第一次同步充电数据，所以本次充电费用也就是目前充电的总费用。
////                    BigDecimal chargingCost = getCost(chargingCapacity,powerFee);
////                    log.debug("当前时间(小时){}，计价规则名称{},电费单价:{},充电度数:{},本次花费总费用:{}", hour,RuleName,powerFee,chargingCapacity,chargingCost);
////                    //3.给ChargingData中存入总花费： 本次同步充电信息的总花费就是充电的总花费(包含之前的总花费)
////                    chargingData.setTotalCost(chargingCost);//给充电记录中保存目前的充电总费用。
////                    //4.将本次同步的充电数据对象chargingProcessParam存入到chargingData(作为上一次的充电的数据)
////                    chargingData.setLastChargingProcessParam(chargingProcessParam);//给充电记录中保存最后一次同步的充电进度数据。*/
////
////                    //将本if语句中的所有代码提取到下边的方法内了
////                    extracted1(chargingProcessParam, powerFee, hour, RuleName, chargingData);
////                }else{
////                    extracted2(chargingProcessParam, chargingData, powerFee, hour, RuleName);
////
////                    //将下边所有代码提取到上边的extracted2()方法中了
////                    /*//如果不是第一次同步充电进度数据，就用chargingData获取上一次同步的充电数据
////                    ChargingProcessParam lastChargingProcess = chargingData.getLastChargingProcessParam();
////                    log.debug("通过订单号orderNo从充电记录对象chargingData中获取上一次同步的充电数据， 如果获取到就表示不是第一次同步充电数据");
////                    //如果每次同步的充电进度数据都不包含上一次的充电数据，则需要用每次充电数据累加。
////                    //比如，第一次同步 充了3度电， 第二次同步充了1度电， 第三次同步充了2度电， 则充电的总度数就是:  3+1+2=6度电。
////
////                    //如果每次同步的充电进度数据都包含上一次的充电数据，则只需要取最后一次同步的充电度数数据。
////                    //比如，第一次同步 充了3度电， 第二次同步充了4度电(第二次充了1度电，包含第一次的3度电就是4度电)， 第三次同步充了6度电， 则充电的总度数就是:  6度电。
////                    //这时计算充电总费用的公式就是： (3*平的电费单价) +( (4-3)*尖的电费单价) + (6-4*高峰的电费单价)
////
////                    //获取上一次的充电度数。
////                    float lastChargingCapacity=lastChargingProcess.getChargingCapacity();
////                    //获取本次的充电度数(最新的充电度数<通过方法入参获取>)
////                    float currentChargingCapacity =chargingProcessParam.getChargingCapacity();
////                    //计算本次的充电度数： currentChargingCapacity - lastChargingCapacity
////                    // (因为本次同步的是充电的总度数，包含上一次的充电度数， 本次同步的充电总度数 - 上一次同步的充电总度数 就是本次充电的实际度数)
////                    float realChargingCapacity = currentChargingCapacity - lastChargingCapacity;*/
////                    //上边的一段代码优化到了getRealChargingCapacity()方法中了。//替换成下边的一行代码了
////                    //1.计算本次实际的充电度数(上一次同步和本次同步之间实际的充电度数)。
////                    /*float realChargingCapacity=getRealChargingCapacity(chargingProcessParam, chargingData);
////
////
////                    //将本次真实的充电度数转化为BigDecimal类型。
////                    //把float类型的充电度数realChargingCapacity转换为BigDecimal类型的chargingCapacityBigDecimal。
////            //                    BigDecimal realChargingCapacityBigDecimal=new BigDecimal(Float.toString(realChargingCapacity));
////                    //上边类型转化的代码不需要了(使用了getCost()方法就不需要这个代码了被删除了)
////
////
////                    //2.用getCost()计算本次的充电费用chargingCost ： 单位电价 * 本次充电度数
////            //                    BigDecimal currentChargingCost = powerFee.multiply(realChargingCapacityBigDecimal);
////                    //优化成下边格式//使用getCost()封装乘法了
////                    BigDecimal currentChargingCost = getCost(realChargingCapacity, powerFee);
////
////                    log.debug("当前时间(小时){}，计价规则名称{},电费单价:{},本次充电度数:{},本次花费总费用:{}", hour, RuleName, powerFee,realChargingCapacity,currentChargingCost);
////
////                    //3.用本次的充电费用currentChargingCost和之前的充电总费用累加计算目前充电的总费用。
////                    BigDecimal totalCost = chargingData.getTotalCost();//获取上次的花费
////                    BigDecimal currentTotalCost=totalCost.add(currentChargingCost);//上次的总花费 + 本次花费
////                    //4.给ChargingData中存入目前的总花费： 本次同步充电信息的总花费就是充电的总花费(包含之前的总花费)
////                    chargingData.setTotalCost(currentTotalCost);
////                    //5.将本次同步的充电数据对象chargingProcessParam存入到chargingData(作为上一次的充电的数据)
////                    //给ChargingData中存入本次同步的充电进度数据（为下一次同步充电进度数据 计算充电费用时做准备）。
////                    chargingData.setLastChargingProcessParam(chargingProcessParam);*/
////                }
////
////            }
////        }
////
////
////        //每次同步充电数据时，包含上一次的充电数据：
////        //[(3*平的价格)] + [(4-3) * 尖的价格] + [(6-4) * 峰的价格]
////        //次数
////        //充电度数
////        //计价名称
////        //
////        //1次同步充电数据      3       平
////        //2次同步充电数据      4       尖
////        //3次同步充电数据      6       峰
////        //
////        //充电的总度数： 6
////
////
//////        ChargingProcessVO chargingProcessVO=new ChargingProcessVO();
//////        chargingProcessVO.setGunId(chargingProcessParam.getGunId());
//////        return chargingProcessVO;
////        chargingData.setTotalCapacity(chargingProcessParam.getChargingCapacity());//设置充电总度数
////        chargingData.setOrderNo(chargingProcessParam.getOrderNo());
////        chargingData.setGunId(chargingProcessParam.getGunId());
////        return buildChargingProcessVO(chargingData, chargingProcessParam);
////    }
//
//
//    /**
//     * 此方法只是是给上边的calculateCost()方法调用的(其他地方没调用此方法)
//     * 根据当前同步充电数据的时间，判断属于哪个计价规则。
//     * @return
//     */
//    private CostRulePO getMatchCostRuleByTime(Integer stationId, int hour){
//        log.debug("获取计价规则:入参:{}",stationId);
//        List<CostRulePO> costRules=getCostRule(stationId);//通过充电站站id去mysql数据库获取计价列表
//        log.debug("获取计价规则:入参:{},出参:{}",stationId,costRules);
//        log.debug("获取当前时间 小时 :{} 通过当前小时 来计算 当前小时 属于 哪个时间段 尖 峰 平 谷 计算价格",hour);
//        for(CostRulePO costRulePO:costRules){
//            //判断当前小时属于哪个时间段
//            //if(当前时间 大于等于计价规则的开始时间   &&  小于 结束时间)
//            if(costRulePO.getStartTime() <= hour  && hour< costRulePO.getEndTime()){//判断同步的充电进度数据是否是第一次的数据。
//                return costRulePO;
//            }
//        }
//        return null;
//    }
//
//
//
//    /**
//     * 根据订单编号orderNo从Map集合中获取充电记录数据对象chargingData
//     * (如果Map集合中有此对象就返回，没有chargingData,则初始化一个chargingData对象存入到Map集合中,并返回chargingData)
//     * @param chargingProcessParam chargingProcessParam
//     * @return chargingData
//     */
//    private ChargingData buildChargingData(ChargingProcessParam chargingProcessParam) {
//        String orderNo = chargingProcessParam.getOrderNo();//获取订单号
//        ChargingData chargingData = chargingProcessData.get(orderNo);//根据订单号从Map集合中获取chargingData
//        if(chargingData==null) {//用于判断是否同步充电进度数据了
//            chargingData = initChargingData(chargingProcessParam);
//            return chargingData;
//        }else {
//            return chargingData;
//        }
//    }
//
//    /**
//     * 本方法是给上边方法的if分支调用的(其他地方没用到本方法)
//     * 创建一个chargingData充电数据对象初始化存入到Map集合中, 并返回此充电数据对象chargingData。
//     * 初始化充电记录数据(ChargingData对象，用来标识是否是第一次充电，如果是第一次充电就给Map中存入一个ChargingData对象，
//     * 如果不是第一次则Map集合中对于这条订单就会对应一条ChargingData对象)：
//     *   订单和设备的基本信息可以在这里初始化,设备的动态信息 比如充电度数,比如温度不能在这初始化
//     *   因为这的代码 同一个订单号,只执行一次(同一个订单只有一条充电记录)
//     * @param chargingProcessParam 最后一条充电进度数据
//     * @return
//     */
//    private ChargingData initChargingData(ChargingProcessParam chargingProcessParam) {
//        String orderNo = chargingProcessParam.getOrderNo();//从充电进度数据对象中获取订单编号orderNo
//        ChargingData  chargingData = new ChargingData();
//        log.debug("通过订单号:{}，从Map集合中没有获取到充电数据，表示本次是第一次同步充电数据", orderNo);
//        //创建一个chargingData充电数据对象初始化存入到Map集合中, 并返回此充电数据对象chargingData。
//        chargingData.setOrderNo(orderNo);//设置订单号
//        //BigDecimal cost = getCost(chargingProcessParam);//获取本次的充电度数
//        //把当前当前的充电度数(也是充电总金额)设置到chargingData中
//        //注意： 讲师为什么这样设置价格不知道，但是如果1块钱一度电，则充电的总度数就是充电的总金额
//        //chargingData.setTotalCost(cost);
//        //设置开始充电时间。//设置实际充电开始时间和订单创建时间是有点小区别的
//        chargingData.setStartTime(LocalDateTime.now());
//        chargingData.setGunId(chargingProcessParam.getGunId());//设置充电枪id
//        chargingData.setUserId(chargingProcessParam.getUserId());//设置用户id(本条数据属于哪个用户)
//        //本订单的充电数据对象chargingData保存到Map集合中。
//        chargingProcessData.put(orderNo, chargingData);
//        return chargingData;
//    }
//
//
//
//
//
//
//    /**
//     * 计算的第一次的充电价格并设置到ChargingData中： 不需要累加每次的充电费用。
//     * @param chargingProcessParam 表示本次同步的充电数据，用来获取充电的度数， 同时用于充当上一次的充电数据。
//     * @param powerFee 单位电价，用来和chargingProcessParam获取的充电度数相乘。
//     * @param hour 用于log.debug()打印。
//     * @param RuleName 用于log.debug()打印。
//     * @param chargingData 用于保存充电总费用和上一次充电数据。
//     */
//    private void appendFirstCostToChargingData(ChargingProcessParam chargingProcessParam,ChargingData chargingData, BigDecimal powerFee, int hour, String RuleName) {
//        //这个if通过从map中获取ChargingData，然后从chargingData中获取上一次同步的充电数据<如果没有则表示第一次同步充电数据>。
//        //这个if和上边的if是重复的逻辑代码，后边会简化掉。
//        // (A.因为如果是第一次同步充电数据则Map集合中就不会有这个ChargingData对象<表示第一次同步充电数据，但还没有向Map集合中放ChargingData>,
//        //  B.如果Map中有ChargingData对象但此对象中没有LastChargingProcessParam，则表示第一次同步充电数据后，已经给Map集合中放了ChargingData)
//        //  这两种情况都表示第一次同步充电数据，所以是重复的。
//        log.debug("通过订单号orderNo从充电记录对象chargingData中获取上一次同步的充电数据， 如果获取不到就表示是第一次同步充电数据11");
//        //获取充电度数。
//
//        //1.获取本次同步的充电度数(这个if分支表示第一次同步充电数据，所以这个充电度数就是本次实际的充电度数)
//        float chargingCapacity = chargingProcessParam.getChargingCapacity();
//        //把float类型的充电度数chargingCapacity转换为BigDecimal类型的chargingCapacityBigDecimal。
//
////                    BigDecimal chargingCapacityBigDecimal = new BigDecimal(Float.toString(chargingCapacity));
//        //上边类型转化的代码不需要了(使用了getCost()方法就不需要这个代码了被删除了)
//
//        //单价powerFee  * 度数chargingCapacityBigDecimal = 本次充电费用
//        //但是有个问题：充电时间比较长，有可能会跨越多个计价规则，每个计价规则单位电价不同，所以计算本次充电总费用计算稿方式需要调整成下边格式：
//        //平时间段的时长  * 平时间段的电价   +     尖下午时间段的时长  * 尖下午时间段的电价 +   峰时间段的时长  * 峰时间段的电价  = 本次充电费用
//
//
////                    BigDecimal chargingCost = powerFee.multiply(chargingCapacityBigDecimal);
//        //优化成下边格式//使用getCost()封装乘法了
//        //2.用getCost()计算本次的充电费用chargingCost ： 单位电价 * 本次充电度数
//        //注意： 因为这个if表示第一次同步充电数据，所以本次充电费用也就是目前充电的总费用。
//        BigDecimal chargingCost = getCost(chargingCapacity, powerFee);
//        log.debug("当前时间(小时){}，计价规则名称{},电费单价:{},充电度数:{},本次花费总费用:{}", hour, RuleName, powerFee,chargingCapacity,chargingCost);
//        //3.给ChargingData中存入总花费： 本次同步充电信息的总花费就是充电的总花费(包含之前的总花费)
//        chargingData.setTotalCost(chargingCost);//给充电记录中保存目前的充电总费用。
//        //4.将本次同步的充电数据对象chargingProcessParam存入到chargingData(作为上一次的充电的数据)
//        chargingData.setLastChargingProcessParam(chargingProcessParam);//给充电记录中保存最后一次同步的充电进度数据。
//    }
//
//
//    /**
//     * 计算的不是第一次的充电价格并设置到ChargingData中： 需要累加每次的充电费用。
//     * @param chargingProcessParam 充电进度数据
//     * @param chargingData 充电记录对象
//     * @param powerFee 单位电价
//     * @param hour 当前同步充电数据的小时数
//     * @param RuleName 计价规则。
//     */
//    private void appendLastCostToChargingData(ChargingProcessParam chargingProcessParam, ChargingData chargingData, BigDecimal powerFee, int hour, String RuleName) {
//        /*//如果不是第一次同步充电进度数据，就用chargingData获取上一次同步的充电数据
//        ChargingProcessParam lastChargingProcess = chargingData.getLastChargingProcessParam();
//        log.debug("通过订单号orderNo从充电记录对象chargingData中获取上一次同步的充电数据， 如果获取到就表示不是第一次同步充电数据");
//        //如果每次同步的充电进度数据都不包含上一次的充电数据，则需要用每次充电数据累加。
//        //比如，第一次同步 充了3度电， 第二次同步充了1度电， 第三次同步充了2度电， 则充电的总度数就是:  3+1+2=6度电。
//
//        //如果每次同步的充电进度数据都包含上一次的充电数据，则只需要取最后一次同步的充电度数数据。
//        //比如，第一次同步 充了3度电， 第二次同步充了4度电(第二次充了1度电，包含第一次的3度电就是4度电)， 第三次同步充了6度电， 则充电的总度数就是:  6度电。
//        //这时计算充电总费用的公式就是： (3*平的电费单价) +( (4-3)*尖的电费单价) + (6-4*高峰的电费单价)
//
//        //获取上一次的充电度数。
//        float lastChargingCapacity=lastChargingProcess.getChargingCapacity();
//        //获取本次的充电度数(最新的充电度数<通过方法入参获取>)
//        float currentChargingCapacity =chargingProcessParam.getChargingCapacity();
//        //计算本次的充电度数： currentChargingCapacity - lastChargingCapacity
//        // (因为本次同步的是充电的总度数，包含上一次的充电度数， 本次同步的充电总度数 - 上一次同步的充电总度数 就是本次充电的实际度数)
//        float realChargingCapacity = currentChargingCapacity - lastChargingCapacity;*/
//        //上边的一段代码优化到了getRealChargingCapacity()方法中了。//替换成下边的一行代码了
//        //1.计算本次实际的充电度数(上一次同步和本次同步之间实际的充电度数)。
//        float realChargingCapacity=getRealChargingCapacity(chargingProcessParam, chargingData);
//
//
//        //将本次真实的充电度数转化为BigDecimal类型。
//        //把float类型的充电度数realChargingCapacity转换为BigDecimal类型的chargingCapacityBigDecimal。
////                    BigDecimal realChargingCapacityBigDecimal=new BigDecimal(Float.toString(realChargingCapacity));
//        //上边类型转化的代码不需要了(使用了getCost()方法就不需要这个代码了被删除了)
//
//
//        //2.用getCost()计算本次的充电费用chargingCost ： 单位电价 * 本次充电度数
////                    BigDecimal currentChargingCost = powerFee.multiply(realChargingCapacityBigDecimal);
//        //优化成下边格式//使用getCost()封装乘法了
//        BigDecimal currentChargingCost = getCost(realChargingCapacity, powerFee);
//
//        log.debug("当前时间(小时){}，计价规则名称{},电费单价:{},本次充电度数:{},本次花费总费用:{}", hour, RuleName, powerFee,realChargingCapacity,currentChargingCost);
//
//        //3.用本次的充电费用currentChargingCost和之前的充电总费用累加计算目前充电的总费用。
//        BigDecimal totalCost = chargingData.getTotalCost();//获取上次的花费
//        BigDecimal currentTotalCost=totalCost.add(currentChargingCost);//上次的总花费 + 本次花费
//        //4.给ChargingData中存入目前的总花费： 本次同步充电信息的总花费就是充电的总花费(包含之前的总花费)
//        chargingData.setTotalCost(currentTotalCost);
//        //5.将本次同步的充电数据对象chargingProcessParam存入到chargingData(作为上一次的充电的数据)
//        //给ChargingData中存入本次同步的充电进度数据（为下一次同步充电进度数据 计算充电费用时做准备）。
//        chargingData.setLastChargingProcessParam(chargingProcessParam);
//    }
//
//
//    /**
//     * 计算上一次同步到本次同步数据之间，实际的充电度数:
//     * @param chargingProcessParam (本次同步的充电数据<包含所有次同步的充电度数>) 当前同步数据的入参，包含当前同步充电的度数
//     * @param chargingData (上次同步的充电数据)上传同步数据的入参，包含上次同步充电的数据的对象chargingProcessParam
//     * @return  上次同步到本次同步之间充电度数=包含所有次同步的充电度数 - 上次同步的充电数据
//     */
//    private float getRealChargingCapacity(ChargingProcessParam chargingProcessParam,ChargingData chargingData){
//        ChargingProcessParam lastChargingProcessParam = chargingData.getLastChargingProcessParam();//上一次的充电度数
//        float lastChargingCapacity= lastChargingProcessParam.getChargingCapacity();//获取上一次的充电度数
//        float currentChargingCapacity = chargingProcessParam.getChargingCapacity();//全部的充电度数
//        //计算实际的充电度数   : 本次同步的全部充电度数 - 上一次同步的充电度数
//        if(lastChargingProcessParam==null){
//            return currentChargingCapacity;
//        }else {
//            return currentChargingCapacity - lastChargingCapacity;//本次同步的全部充电度数 - 上一次同步的充电度数
//        }
//    }
//
//
//    /**
//     * 从chargingProcessParam中获取本次充电的度数: 将float类型转化为BigDecimal类型。
//     * 计算总费用： 单价*充电总度数
//     * @param chargingCapacity 充电的度数
//     * @param powerFee 单价
//     * @return  总花费=单价*总度数
//     * 注意：将计算总充电费用的代码封装到getCost()方法中，后边如果要计算总充电费用时只需要在getCost方法中添加即可
//     * (不需要在calculateCost()方法里找到对应地方添加)。
//     */
//    private BigDecimal getCost(float chargingCapacity, BigDecimal powerFee){
//        String floatStr = Float.toString(chargingCapacity);
//        //将float类型的充电总度数转化为BigDecimal类型。
//        BigDecimal chargingCapacityBD = new BigDecimal(floatStr);
//        return powerFee.multiply(chargingCapacityBD);//return 总花费=单价*总度数
//    }
//
//
//    /**
//     * 构建一个chargingProcessVO
//     * (业务层给控制层返回充电进度结果信息的值对象，里边包含：充电总时长，充电总度数，本次同步充电数据时的单位电价，充电总费用，订单id，充电枪id)
//     * @param chargingData Map中的充电记录数据(用户的一次充电消费对应Map集合中的一个chargingData对象<key是订单id>)
//     * @param chargingProcessParam 本次同步的充电数据
//     * @return ChargingProcessVO 充电进度值对象
//     */
//    private ChargingProcessVO buildChargingProcessVO(ChargingData  chargingData,ChargingProcessParam chargingProcessParam) {
//        ChargingProcessVO chargingProcessVO=new ChargingProcessVO();
//        chargingProcessVO.setTotalCost(chargingData.getTotalCost());//总花费
//        chargingProcessVO.setOrderNo(chargingData.getOrderNo());//订单编号
//        chargingProcessVO.setGunId(chargingData.getGunId());//充电枪id
//        //ChargingData中的价格就是当前的价格，因为充电可能会跨越多个计费阶段。  价格= 总度数/总费用。
//        chargingProcessVO.setPowerFee(chargingData.getCurrentOneFee());//单位电价。
//        chargingProcessVO.setTotalCapacity(chargingData.getTotalCapacity());//充电的总度数。
//
//        Duration between = Duration.between(chargingData.getStartTime(), LocalDateTime.now());
//        String totalTime=between.toDays() +"-" +  between.toHours() +"-" +  between.toMinutes() +"-"+between.toMillis();
//        chargingProcessVO.setTotalTime(totalTime);
//
//
//        //chargingProcessVO.setUserId(chargingProcessParam.getUserId());
//        //chargingProcessVO.setTotalCost(getCost(chargingProcessParam));
//
//        return chargingProcessVO;
//
//    }
//
//
//
//
//
//    @Autowired(required = false)
//    private CostRuleRepository costRuleRepository;
//
//    @Autowired(required = false)
//    private CostRuleRepository costRuleCacheRepository;//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//    /**
//     * 通过充电站站id去mysql数据库获取计价列表
//     * 设备会不停的给订单同步充电进行数据，则计价服务需要不断的通过充电站站id去mysql数据库获取计价列表
//     * (这样做如果充电桩特别多并且每个都会不停的同步数据，这样太耗时也会导致mmysql并发量太大。
//     * 所以后边把这里改成从redis中获取计价规则了<把mysql中的计价规则从mysql加载到redis中缓存了>)
//     * @param stationId
//     * @return 根据充电站id获取多个计价规则
//     */
//    private List<CostRulePO> getCostRule(Integer stationId) {
//        log.debug("通过站点id:{}从redis获取计价规则",stationId);
//        //从Redis中获取计价规则的数据
//        List<CostRulePO> cacheCostRules=costRuleCacheRepository.getCostRuleByStationId(stationId);
//        //判断redis是否有数据
//        if(!CollectionUtils.isEmpty(cacheCostRules)){
//            log.debug("通过站点id:{}从redis获取计价规则,redis有数据 直接返回",stationId);
//            return cacheCostRules;
//        }else{
//            log.debug("通过站点id:{}从 Redis没有查询到数据 从数据库查询",stationId);
//            //如果Redis没有计价规则数据，则从mysql数据库中获取计价规则的数据
//            List<CostRulePO> dbCostRules = costRuleRepository.getCostRuleByStationId(stationId);
//            //判断数据库是否有数据
//            if(!CollectionUtils.isEmpty(dbCostRules)){
//                //数据库有数据 写入redis
//                log.debug("通过站点id:{} 数据库有数据 写入到Redis,并返回",stationId);
//                //将从数据库查询出的dbCostRules数据，存入到redis中
//                //costRuleRepository.saveCostRule(stationId,dbCostRules);//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//                return dbCostRules;
//            }
//        }
//        return Collections.emptyList();//否则就返回空集合
//    }
//
//
//
//}
//
//
////理论50% 实战50%
////AI课程占比85%  4/5
//
//
//
//
//
//
//
//
